package com.gimi.cloud.bbp.gateway.filters.pre;

import java.net.URLEncoder;
import java.util.Base64;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.gimi.cloud.bbp.common.constant.RedisKeyConstant;
import com.gimi.cloud.bbp.common.enums.ResponseCodeEnum;
import com.gimi.cloud.bbp.common.utils.IPUtil;
import com.gimi.cloud.bbp.common.utils.JsonUtil;
import com.gimi.cloud.bbp.common.utils.StringUtil;
import com.gimi.cloud.bbp.common.utils.reqhead.Response;
import com.gimi.cloud.bbp.common.utils.reqhead.Util;
import com.gimi.cloud.bbp.commonService.redis.RedisService;
import com.gimi.cloud.bbp.dto.sys.SysMasterDTO;
import com.gimi.cloud.bbp.gateway.service.CheckMasterService;
import com.gimi.cloud.bbp.gateway.service.CheckTokenService;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.nimbusds.jose.JOSEObject;
import com.nimbusds.jose.util.Base64URL;

/**
 * Created by luck on 2017/09/28. 该过滤器覆盖范围为整个微服务.
 */
@Component
public class PreFilter extends ZuulFilter {
	private static Logger logger = LoggerFactory.getLogger(PreFilter.class);
	private static String[] headerKeys = { "Service-Name", "Authorization" };
	private final RedisService redisService;
	private final CheckTokenService checkTokenService;
	private final CheckMasterService checkMasterService;

	static {
	}

	public PreFilter(RedisService redisService,CheckTokenService checkTokenService, CheckMasterService checkMasterService) {
		this.redisService = redisService;
		this.checkTokenService = checkTokenService;
		this.checkMasterService = checkMasterService;
	}

	@Override
	public String filterType() {
		return "pre";
	}

	@Override
	public int filterOrder() {
		return 1;
	}

	@Override
	public boolean shouldFilter() {
		return true;
	}

	@Override
	public Object run() {
		logger.info("===>进入gateway过滤器!");
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		String requestUrl = request.getRequestURL().toString();
		logger.info(String.format("%s request to %s", request.getMethod(), requestUrl));
		Map<String, String> heads = getHeader(request, headerKeys);
		DiskFileItemFactory factory = new DiskFileItemFactory();
		ServletFileUpload upload = new ServletFileUpload(factory);
		try {
			if(upload.isMultipartContent(request)){
				Map<String, List<FileItem>> requestParams = upload.parseParameterMap(request);
			    for(String key : requestParams.keySet()){
			    	if("Authorization".equals(key) && "common".equals(requestParams.get(key).get(0).getString("UTF-8"))){
						heads.put("Service-Name", requestParams.get("Service-Name").get(0).getString("utf-8"));
						break;
					}
			    }
			}
		} catch (Exception e) {
		    e.printStackTrace();
		}
		if("common".equals(request.getParameter("Authorization"))){
			heads.put("Service-Name", request.getParameter("Service-Name"));
		}
		// 此处预留 验签 鉴权 取出jwt token验证(从redis取出该user对应秘钥)
		// 取出token后验证通过,设置相应的user_id到httpRequestHeader里面,方便feign层取出进行调用,注意user_id不从外部传入
		String serviceName = heads.get("Service-Name");
		String token = heads.get("Authorization");
		Response resp = null;
		RequestContext requestContext = RequestContext.getCurrentContext();
		requestContext.addZuulRequestHeader("authorization", heads.get("Authorization"));
		requestContext.addZuulRequestHeader("serviceName", heads.get("Service-Name"));
		requestContext.addZuulRequestHeader("ip", IPUtil.getRemortIP(request));
		requestContext.addZuulRequestHeader("authToken",heads.get("Authorization"));
		String serviceType = getServiceType(requestContext, requestUrl);
		switch (serviceType) {
		case "manage":
			requestContext = manageFilter(requestContext, token, serviceName);
			break;
		case "merchant":
			requestContext = manageFilter(requestContext, token,serviceName);
			break;
		case "app":
			requestContext = appFilter(requestContext, token, resp, serviceName);
			break;
		default:
			break;
		}
		logger.info("token校验完成\n");
		return null;
	}

	/**
	 * 后台管理系统过滤器
	 * @param requestContext
	 * @param token token校验信息
	 * @param serviceName 当前请求服务名
	 * @return 校验结果，如果为空，返回null
	 */
	private RequestContext manageFilter(RequestContext requestContext, String token, String serviceName) {
		//如果服务名为空，则直接返回
		if (StringUtil.isBlank(serviceName)) {
			logger.info(ResponseCodeEnum.SERVICE_NAME_NOT_FOUND.getDesc());
			sendResponse(requestContext, ResponseCodeEnum.SERVICE_NAME_NOT_FOUND);
			return null;
		}
		//获取Manage开放服务信息
		JSONArray manageServiceNameList = redisService.getJSONArrayConfigByKey(RedisKeyConstant.CONFIG_KEY_MANAGE_OPEN_SERVICE);
		//如果是开放服务，则直接跳过后续验证
		if(manageServiceNameList.contains(serviceName)){
			logger.info(serviceName+"在Manage开放Service名单中，直接跳过token校验");
			return requestContext;
		}
		//校验ManageToken信息
		ResponseCodeEnum checkToken = checkTokenService.checkToken(token, "manage");
		//校验服务不通过，直接返回
		if (checkToken != null) {
			sendResponse(requestContext, checkToken);
			return null;
		}
		Map payload = getPayload(token);
		String userName = (String) payload.get("userName");
		//如果用户名称为空，则直接返回无数据
		if(null == userName){
			sendResponse(requestContext, ResponseCodeEnum.ERR_ACCOUNT_HAVE_NOT_ID);
			return null;
		}
		//校验当前用户登录Token
		String redisKey = RedisKeyConstant.MANAGE_LOGIN_MASTER_TOKEN_PREFIX +userName;
		JSONArray masterLoginTokens = redisService.getJSONArrayByKey(redisKey);
		//判断当前token是否在redis存在的用户token中
		if(null == masterLoginTokens || !masterLoginTokens.contains(token)){
			sendResponse(requestContext, ResponseCodeEnum.ERR_ACCOUNT_HAVE_NOT_ID);
			return null;
		}
		//获取当前用户登录信息
		SysMasterDTO masterDTO = checkMasterService.getMasterByUserName(userName);
		//如果用户信息不存在
		if (masterDTO == null) {
			sendResponse(requestContext, ResponseCodeEnum.ERR_USER_INVALID);
			return null;
		}
//		JSONObject permission = redisService.getJsonObjectByKey(RedisKeyConstant.MANAGE_LOGIN_MASTER_PERMISSION_PREFIX+masterDTO.getMaId());
//		if(null == permission){
//			sendResponse(requestContext, ResponseCodeEnum.NO_PERMISSION);
//			return null;
//		}
		//获取所有的有权限的服务名
//		JSONArray serviceNames = permission.getJSONArray("serviceNames");
//		if(null == serviceNames || !serviceNames.contains(serviceName)){
//			sendResponse(requestContext, ResponseCodeEnum.NO_PERMISSION);
//			return null;
//		}
		requestContext.addZuulRequestHeader("uid",String.valueOf(masterDTO.getMaId()));
		try{
			requestContext.addZuulRequestHeader("uname", URLEncoder.encode(masterDTO.getMaName(),"utf-8"));
		}catch(Exception e){
			e.printStackTrace();
		}
		return requestContext;
	}

	/**
	 * app的接口过滤方法
	 * @param requestContext
	 * @param token 当前用户token
	 * @return 过滤结果，如果异常，则返回空wbj
	 *
	 */
	private RequestContext appFilter(RequestContext requestContext, String token, Response resp, String serviceName) {
		if (StringUtil.isBlank(serviceName)) {
			logger.info(ResponseCodeEnum.SERVICE_NAME_NOT_FOUND.getDesc());
			sendResponse(requestContext, ResponseCodeEnum.SERVICE_NAME_NOT_FOUND);
			return null;
		}
		//获取APP开放服务信息
		JSONArray serviceNameList = redisService.getJSONArrayConfigByKey(RedisKeyConstant.CONFIG_KEY_APP_OPEN_SERVICE);
		//如果是开放服务，则直接跳过后续验证
		if(serviceNameList.contains(serviceName)){
			return requestContext;
		}
		JSONObject appUserTokenRule = redisService.getJSONObjectConfigByKey(RedisKeyConstant.CONFIG_KEY_APP_USER_TOKEN_RULE);
		if(null == appUserTokenRule){
			logger.info("网关获取app用户token规则异常,系统配置错误");
			sendResponse(requestContext, ResponseCodeEnum.ERROR_SYSTEM_CONFNIG);
			return null;
		}
		String userId = "0";
		resp = new Response();
		//校验token
		ResponseCodeEnum responseCodeEnum = checkTokenService.checkToken(token, "app");
		//如果校验非空，则直接返回
		if(null != responseCodeEnum){
			resp.setResponseCode(responseCodeEnum);
			requestContext.setSendZuulResponse(false);
			requestContext.setResponseStatusCode(200);
			Util.sendResponse(JSONObject.toJSONString(resp), requestContext.getResponse());
			return null;
		}
		//获取token中携带的用户信息
		userId = getUserId(token);
		if(null == userId || "0".equals(userId)){
			logger.info("获取token中的用户信息异常，获取结果userId："+userId);
			sendResponse(requestContext, ResponseCodeEnum.NO_DATA);
			return null;
		}
		String userTokenRedisKey = RedisKeyConstant.APP_USER_TOKEN_PREFIX +userId;
		//获取用户当前的token信息
		JSONArray userTokens = redisService.getJSONArrayByKey(userTokenRedisKey);
		//如果用户登录token为空
		if(null == userTokens){
			sendResponse(requestContext, ResponseCodeEnum.SIGNATURE_TIMEOUT);
			return null;
		}else if(!userTokens.contains(token)){
			sendResponse(requestContext, ResponseCodeEnum.LOGIN_REPEAT_ERROR);
			return null;
		}
		//将当前登录用户的用户id设置到请求头中
		requestContext.addZuulRequestHeader("userId", userId);
		requestContext.addZuulRequestHeader("_userId", userId);
		return requestContext;
	}

	private String getServiceType(RequestContext context, String requestUrl) {
		if (StringUtil.isBlank(requestUrl)) {
			sendResponse(context, ResponseCodeEnum.SERVICE_NAME_NOT_FOUND);
		}
		String[] arr = requestUrl.split("/");
		if (arr == null || arr.length < 2) {
			sendResponse(context, ResponseCodeEnum.SERVICE_NAME_NOT_FOUND);
		}
		return arr[arr.length - 2];
	}

	/**
	 * 获取header信息
	 * @param req request请求
	 * @param keys 键集合
	 * @return value
	 */
	private Map<String, String> getHeader(HttpServletRequest req, String[] keys) {
		Map<String, String> headerMap = new HashMap<>();
		for (String key : keys) {
			if (key.equals("Authorization")) {
				String tokenStr = req.getHeader(key);
				if (tokenStr != null && tokenStr.length() > 7) {
					tokenStr = tokenStr.substring(7, tokenStr.length());
				}
				headerMap.put(key, tokenStr);
			} else {
				headerMap.put(key, req.getHeader(key));
			}
		}
		return headerMap;
	}

	/**
	 * 根据token获取用户id
	 * @param token 用户登录token
	 * @return 获取到的用户id
	 */
	private String getUserId(String token) {
		String result = "0";
		try {
			Base64URL second = JOSEObject.split(token)[1];
			String payload = new String(Base64.getDecoder().decode(second.toString()), "utf-8");
			result = JSONObject.parseObject(payload).getString("userId");
		} catch (Exception e) {
			logger.error("获取userId异常：" + e.getMessage());
		}
		return result;
	}

	private Map getPayload(String token) {
		String result = "{}";
		try {
			Base64URL second = JOSEObject.split(token)[1];
			result = new String(Base64.getDecoder().decode(second.toString()), "utf-8");
		} catch (Exception e) {
			logger.error("获取userId异常：" + e.getMessage());
		}
		return JsonUtil.jsonToMap(result);
	}

	private void sendResponse(RequestContext ctx, ResponseCodeEnum status) {
		Response resp = new Response();
		ctx.setSendZuulResponse(false);
		ctx.setResponseStatusCode(200);
		resp.setResponseCode(status);
		Util.sendResponse(JSONObject.toJSONString(resp), ctx.getResponse());
	}

}
